/* 
    sample code for JPEG for 7113 composite input and JPEG output
    This sample code is to do encode five JPEG files named "/tmp/img0.jpg"...
 */
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/timeb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/ipc.h>
#include <sys/socket.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <linux/videodev.h>
#include <errno.h>
#include <linux/fb.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/un.h>
#include <signal.h>
#include "fmjpeg_avcodec.h"


#define FMJPEG_ENCODER_DEV  "/dev/mjenc" //major:10 minior:61


typedef struct AVFrame {
    uint8_t *data[4];
} AVFrame;

typedef struct video_profile
{
    unsigned int bit_rate;
    unsigned int width;   //length per dma buffer
    unsigned int height;
    unsigned int framerate;
    unsigned int frame_rate_base;
    unsigned int gop_size;
    unsigned int qmax;
    unsigned int qmin;   
    unsigned int quant;
    unsigned int fmjpeg_image_quality;
    unsigned int u32RestartInterval;
    unsigned int fmjpeg_jpeg;
    unsigned int fmjpeg_yuv_format;
    unsigned int YUV_2D;	
    int roi_enable;
    unsigned int roi_left_x;
    unsigned int roi_left_y;
    unsigned int roi_right_x;
    unsigned int roi_right_y;	
    AVFrame *coded_frame;
} video_profile;

video_profile           video_setting;
int fmjpeg_enc_fd=0;
FJPEG_ENC_PARAM enc_param;

/*  
#################################################
    capture 
################################################# 
 */
typedef struct fcap_frame_buff
{
    unsigned int phyAddr;
    unsigned int mmapAddr;   //length per dma buffer
    unsigned int frame_no;
}fcap_frame_buff_t;
int video_fd;
int frame_num;
struct video_mbuf       gb_buffers;
struct video_mmap       gb_buf;
struct video_capability video_cap;
struct video_window     win;
struct video_tuner      tuner;  


unsigned char           *video_buf_virt;
unsigned char           *out_virt_buffer;
unsigned char           mmap_addr;
unsigned int            mmap_done=0;
unsigned char        yuv_format;

/* seq=0 for 2D capture, seq=1 for sequential */
int v4l_init(video_profile *profile)
{
    int type,ingrab;
    video_fd = open("/dev/video0", O_RDWR);

    if(video_fd < 0)  {
        printf("open device failure");
        return -1;
    }
    if (!ioctl(video_fd, VIDIOCGTUNER, &tuner))  {
        tuner.mode = VIDEO_MODE_NTSC;
        ioctl(video_fd, VIDIOCSTUNER, &tuner);
    }
	
    if ( ioctl(video_fd,VIDIOCGMBUF,&gb_buffers) < 0 ) {
		printf("capture not support mbuf\n");
		return -1;
	}


    if(mmap_done==0) {
        mmap_done = 1;
        video_buf_virt = mmap(0, gb_buffers.size, PROT_READ|PROT_WRITE, MAP_SHARED, video_fd, 0);
	}

    ingrab=7; //using 7113 Composite on 8120
    ioctl(video_fd, 0x5679, &ingrab);            //grab type
    type = yuv_format;
    ioctl(video_fd, 0x5678, &type); 
    //JPEG need sequential format
    // 0 for Mpeg4 2D YUV
    // 1 for JPG Sequencoal YUV
    // 2 for H.264 2D
    
    gb_buf.height=profile->height;
    gb_buf.width=profile->width;
    gb_buf.format=VIDEO_PALETTE_YUV420P;
    gb_buf.frame=0;    
    return 0;
}


#define IMAGE_COMP		3
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define MIN(x, y) ((x) < (y) ? (x) : (y))

#define SET_COMP(index,hsamp,vsamp)  \
  (enc_param.rgComponentInfo[index].m_u8HSamplingFrequency = (hsamp), \
   enc_param.rgComponentInfo[index].m_u8VSamplingFrequency = (vsamp))

struct mdcfg {
    unsigned int    exist;
    pid_t           pid;
    unsigned int    md_interval;
    unsigned int    x0_LU;
    unsigned int    y0_LU;
    unsigned int    x0_RD;
    unsigned int    y0_RD;
    
    unsigned int    x1_LU;
    unsigned int    y1_LU;
    unsigned int    x1_RD;
    unsigned int    y1_RD;
    
    unsigned int    x2_LU;
    unsigned int    y2_LU;
    unsigned int    x2_RD;
    unsigned int    y2_RD;

    unsigned int    mv_th0;
    unsigned int    sad_th0;
    unsigned int    dev_th0;
    unsigned int    alarm_idx0;

    unsigned int    mv_th1;
    unsigned int    sad_th1;
    unsigned int    dev_th1;
    unsigned int    alarm_idx1;

    unsigned int    mv_th2;
    unsigned int    sad_th2;
    unsigned int    dev_th2;
    unsigned int    alarm_idx2;
    unsigned int     ver;
    unsigned char   filename[30];
    unsigned int    rotate;
    unsigned int    rotate_idx;
    unsigned int    mdt1;
    unsigned int    mdt2;
    unsigned int    mdt3;
    unsigned int    mdt4;	
    unsigned int    mdt5;
} mdcfg;


struct mdcfg mdval;

void fmj_set_motion_cfg(struct mdcfg *val)
{
    	memcpy(&mdval,val,sizeof(struct mdcfg));
}

#if 0
void motion_detection_init(FJPEG_ENC_PARAM *enc_param)
{
	
	enc_param->range_mb_x0_LU = mdval.x0_LU;
  	enc_param->range_mb_y0_LU = mdval.y0_LU;
  	enc_param->range_mb_x0_RD = mdval.x0_RD;
  	enc_param->range_mb_y0_RD = mdval.y0_RD;
  	enc_param->range_mb_x1_LU = mdval.x1_LU;
  	enc_param->range_mb_y1_LU = mdval.y1_LU;
  	enc_param->range_mb_x1_RD = mdval.x1_RD;
  	enc_param->range_mb_y1_RD = mdval.y1_RD;
  	enc_param->range_mb_x2_LU = mdval.x2_LU;
  	enc_param->range_mb_y2_LU = mdval.y2_LU;
  	enc_param->range_mb_x2_RD = mdval.x2_RD;
  	enc_param->range_mb_y2_RD = mdval.y2_RD;
        enc_param->mdt1 = mdval.mdt1;
	enc_param->mdt2 = mdval.mdt2;
	enc_param->mdt3 = mdval.mdt3;
	enc_param->mdt4 = mdval.mdt4;
	enc_param->mdt5 = mdval.mdt5;
  	//enc_param->MV_th0 = mdval.mv_th0;    
  	//enc_param->sad_th0 = mdval.sad_th0;   
  	enc_param->delta_dev_th0 = mdval.dev_th0;   
                                     
  	//enc_param->MV_th1 = mdval.mv_th1;        
  	//enc_param->sad_th1 = mdval.sad_th1;       
  	enc_param->delta_dev_th1 = mdval.dev_th1;       

  	//enc_param->MV_th2 = mdval.mv_th2;      
  	//enc_param->sad_th2 = mdval.sad_th2;     
  	enc_param->delta_dev_th2 = mdval.dev_th2;     

  	enc_param->md_interval = mdval.md_interval;  	

}
#endif


int mjpeg_init(video_profile *video_setting)
{
         unsigned int    yuv_size=0;  
  	unsigned int    max_h_samp,max_v_samp;
  	int             i;
  	int             YUVsampling;
         unsigned int    image_size[3];
    
    
	fmjpeg_enc_fd=open(FMJPEG_ENCODER_DEV,O_RDWR);
	    	
	if(fmjpeg_enc_fd==0) {
	  	printf("Fail to open %s\n",FMJPEG_ENCODER_DEV);
	         fflush(stdout);
	  	return -1;
	} 

  	enc_param.u32ImageQuality=video_setting->fmjpeg_image_quality; // we set image quality to 90 (0~100)  
  	enc_param.u32RestartInterval=video_setting->u32RestartInterval; // we set restart interval to 5  
 	enc_param.u32ImageWidth=video_setting->width;  // set image width
  	enc_param.u32ImageHeight=video_setting->height; // set image height
  	enc_param.u8NumComponents=IMAGE_COMP; // the input image has 3 components 'YUV'  
         enc_param.u8JPGPIC=video_setting->fmjpeg_jpeg;
         enc_param.u82D=video_setting->YUV_2D;          // 0: for Mp4 1: for JPG 2: for H.264                                               
         enc_param.u32ImageMotionDetection=mdval.exist; //avctx->fmjpeg_md;
         enc_param.roi_enable = video_setting->roi_enable;
	enc_param.roi_left_x=video_setting->roi_left_x;
         enc_param.roi_left_y=video_setting->roi_left_y;
         enc_param.roi_right_x=video_setting->roi_right_x;
         enc_param.roi_right_y=video_setting->roi_right_y;	 
		 
  	//YUVsampling = 0; // YUV420
  	//YUVsampling = 1; // YUV422
  	//YUVsampling = 2; // YUV211
  	//YUVsampling = 3; // YUV333
  	//YUVsampling = 4; // YUV222
  	//YUVsampling = 5; // YUV111
        YUVsampling=video_setting->fmjpeg_yuv_format;

	if(enc_param.u8NumComponents==1)
            YUVsampling = 5; // if there is only one component, it is gray, so we force it YUV111
    
	switch (YUVsampling) {
		case 0:
		  SET_COMP(0, 2, 2);
		  SET_COMP(1, 1,1);
		  SET_COMP(2, 1,1);
		  break;
		case 1:
		  SET_COMP(0, 4,1);
		  SET_COMP(1, 2,1);
		  SET_COMP(2, 2,1);
		  break;
		case 2:
		  SET_COMP(0, 2,1);
		  SET_COMP(1, 1,1);
		  SET_COMP(2, 1,1);
		  break;
		case 3:
		  SET_COMP(0, 3,1);
		  SET_COMP(1, 3,1);
		  SET_COMP(2, 3,1);
		  break;
		case 4:
		  SET_COMP(0, 2,1);
		  SET_COMP(1, 2,1);
		  SET_COMP(2, 2,1);
		  break;
		case 5:
		  SET_COMP(0, 1,1);
		  SET_COMP(1, 1,1);
		  SET_COMP(2, 1,1);
		  break;
		default:
		  break;
	}  // to set each component's sampling factor (horizontally and vertically)
  	   
  	// get the maximum horizontal sampling factor
  	max_h_samp=MAX(enc_param.rgComponentInfo[0].m_u8HSamplingFrequency,
             	MAX(enc_param.rgComponentInfo[1].m_u8HSamplingFrequency,
                 enc_param.rgComponentInfo[2].m_u8HSamplingFrequency));
  	// get the maximum horizontal sampling factor
  	max_v_samp=MAX(enc_param.rgComponentInfo[0].m_u8VSamplingFrequency,
             	MAX(enc_param.rgComponentInfo[1].m_u8VSamplingFrequency,
                 enc_param.rgComponentInfo[2].m_u8VSamplingFrequency)); 
                 
         
  	// calculate each component size according to its maximum sampling factor
  	// and individual sampling factor
  	for(i=0;i<enc_param.u8NumComponents;i++) {
             image_size[i]=(((enc_param.rgComponentInfo[i].m_u8HSamplingFrequency*enc_param.u32ImageWidth) /max_h_samp) *
                    ((enc_param.rgComponentInfo[i].m_u8VSamplingFrequency*enc_param.u32ImageHeight) /max_v_samp));
             yuv_size += image_size[i];
         }

  	if ( ioctl(fmjpeg_enc_fd,FMJPEG_IOCTL_ENCODE_CREATE,&enc_param) < 0 ) {
		printf("Error to set FMJPEG_IOCTL_ENCODE_CREATE\n");
        		fflush(stdout);
                  return -1;
  	}
  	return 0;
}


int mjpeg_encode(video_profile *video,uint8_t *buf,void *data)
{
        	AVFrame         *pav=(AVFrame *)data;  
  	int             i;

         if ( video->roi_enable ) {
		 enc_param.roi_left_x = video->roi_left_x;
		 enc_param.roi_left_y = video->roi_left_y;
		 enc_param.roi_right_x = video->roi_right_x;
		 enc_param.roi_right_y = video->roi_right_y;
         }
		 	
	for(i=0;i<enc_param.u8NumComponents;i++)
    enc_param.pu8YUVAddr[i]=(unsigned char *)pav->data[i];  // capture YUV virtual addres

	enc_param.pu8BitstreamAddr=buf;
	if ( ioctl(fmjpeg_enc_fd,FMJPEG_IOCTL_ENCODE_ONE,&enc_param) < 0 ) {
	     printf("Error to set FMJPEG_IOCTL_ENCODE_ONE\n"); 
              fflush(stdout);
              return -1;
         }	

	return enc_param.bitstream_size;	
}


int mjpeg_close(video_profile *video_setting)
{
	if ( ioctl(fmjpeg_enc_fd,FMJPEG_IOCTL_ENCODE_DESTROY,&enc_param) < 0) {
             printf("Error to set FMJPEG_IOCTL_ENCODE_DESTROY\n");
             fflush(stdout);
             return -1;
         }
	close (fmjpeg_enc_fd);
	return 0;
}


unsigned int  encode_video(video_profile *video, uint8_t *jpeg_buff)
{	
    AVFrame             yuv_data;
    unsigned char     *ptr;
    
    if (ioctl(video_fd, VIDIOCMCAPTURE, &gb_buf) < 0) {
        if (errno == EAGAIN)
            fprintf(stderr,"Cannot Sync\n");
        else
            perror("VIDIOCMCAPTURE");
        return 0 ;
    }
    	
    while (ioctl(video_fd, VIDIOCSYNC, &frame_num) < 0 && (errno == EAGAIN || errno == EINTR))
        printf("error\n");
    
    	
    ptr = video_buf_virt + gb_buffers.offsets[frame_num];
    //printf("gb_buf.frame=%d\n", gb_buf.frame);
    frame_num = gb_buf.frame;
    
    yuv_data.data[0] = (unsigned char *)(ptr);
    
    if(video->height % 16 != 0)
        video->height = 16 * ((video->height / 16) + 1 );
    
    if(video->width % 16 != 0)
        video->width = 16 * ((video->width / 16) + 1 );
    
    // UV    	
    yuv_data.data[1] = (unsigned char *) (ptr + (video->width * video->height));
    yuv_data.data[2] = (unsigned char *) (ptr + (video->width *video->height * 5 / 4));
    
#if 0    
    return (unsigned int) mjpeg_encode(video, out_virt_buffer, (void *)&yuv_data);	
#else
    return (unsigned int) mjpeg_encode(video, jpeg_buff, (void *)&yuv_data);	
#endif
}
#define ROI_LEFT_X 		120
#define ROI_LEFT_Y 		120
#define ROI_RIGHT_X 		320
#define ROI_RIGHT_Y 		240

int main(int argc, char **argv)
{
    int ret, i;    
    unsigned int blength;
    char    dout_name[30];
    FILE    *dout;	
//    int optindex;	
    if(argc<3) {
        		printf("Usage: \n");
        		printf("     jpeg_main -f 0: initization without ROI and MP4 2D YUV\n");
		printf("     jpeg_main -f 1: initization without ROI and Jpg sequencial YUV\n");		
        		printf("     jpeg_main -r 0: initization with ROI and MP4 2D YUV\n");
		printf("     jpeg_main -r 1: initization with ROI and Jpg sequencial YUV\n");	
		return 0;
    }
     if (argc > 2)     {
        // something simple for now

	if (strcmp("-f", argv[1]) == 0)
		video_setting.roi_enable = 0;
	else if (strcmp("-r", argv[1]) == 0)
		video_setting.roi_enable = 1;
	yuv_format = atoi(argv[2]);

    }
   	 
    //set the default value
    video_setting.qmax = 31;
    video_setting.qmin = 1;
    video_setting.quant = 0;
    video_setting.bit_rate = 512;
    video_setting.width = 720;
    video_setting.height = 480;
    video_setting.framerate = 5;
    video_setting.frame_rate_base = 1;
    video_setting.gop_size = 30;
    video_setting.fmjpeg_image_quality=40;      //need assign by UI
    video_setting.u32RestartInterval=10;        //need assign by UI
    video_setting.fmjpeg_jpeg=1;        		//0 for motion JPEG, 1 for JPEG
    video_setting.fmjpeg_yuv_format=0;           //0 for YUV420	
    video_setting.YUV_2D = yuv_format;            // 0 for mp4, 1 for jpg, 2 for h264

    video_setting.roi_left_x=ROI_LEFT_X;
    video_setting.roi_left_y=ROI_LEFT_Y;
    video_setting.roi_right_x=ROI_RIGHT_X;
    video_setting.roi_right_y=ROI_RIGHT_Y;


	
    out_virt_buffer = (unsigned char *) malloc(video_setting.width * video_setting.height * 3/2);	
   	
    if((ret=v4l_init(&video_setting)) < 0)
        return -1;

    if(mjpeg_init(&video_setting)<0)
        return -1;

    for(i=0;i<5;i++)  {
	if ( video_setting.roi_enable) {
		video_setting.roi_right_x = ROI_RIGHT_X + i*32;
		video_setting.roi_right_y = ROI_RIGHT_Y + i*32;
		video_setting.roi_left_x = ROI_LEFT_X + i*32;
		video_setting.roi_left_y = ROI_LEFT_Y + i*32;
	}
        if ( video_setting.roi_enable) {
	   if ( yuv_format == 0)		
	          sprintf(dout_name,"/tmp/share/mp4yuv_roi%d.jpg",i);    
	   else if ( yuv_format == 1)
	   	 sprintf(dout_name,"/tmp/share/jpgyuv_roi%d.jpg",i);
	   else if ( yuv_format == 2)
	   	 sprintf(dout_name,"/tmp/share/h264yuv_roi%d.jpg",i);
        } else {		
            if ( yuv_format == 0)		
	          sprintf(dout_name,"/tmp/share/mp4yuv_img%d.jpg",i);
	   else if ( yuv_format == 1)
	   	 sprintf(dout_name,"/tmp/share/jpgyuv_img%d.jpg",i);
	   else if ( yuv_format == 2)
	   	 sprintf(dout_name,"/tmp/share/h264yuv_img%d.jpg",i);
        	}		
        dout=fopen(dout_name,"wb");
        printf("Use output name %s\n",dout_name);fflush(stdout);        
        blength = encode_video(&video_setting, out_virt_buffer);      
        fwrite((void *)out_virt_buffer,blength,1,dout);    		
        fclose(dout);
    }

  	
    if ( mjpeg_close(&video_setting) < 0 )
        printf("mjpeg_close fail\n");		
		
    free(out_virt_buffer);	
		
    close(video_fd);
		
    return 0;
}
